using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;

namespace LibGreen.Jabber.ProtocolElements
{
    /// <summary>
    /// Defines the known values for the "type" attribute of a &lt;message&gt; element
    /// </summary>
    public enum MessageElementType
    {
        /// <summary>
        /// The "type" attribute was not specified.
        /// </summary>
        None,
        /// <summary>
        /// The message was sent in the context of a one-to-one chat conversation.
        /// </summary>
        Chat,
        /// <summary>
        /// An error occured in relation to a previously sent message.
        /// </summary>
        Error,
        /// <summary>
        /// The message was sent in the context of a multi-user chat conversation.
        /// </summary>
        GroupChat,
        /// <summary>
        /// The message is from an automated announcement service.  No reply is expected.
        /// </summary>
        Headline,
        /// <summary>
        /// A normal message, but outside the context of a one-to-one chat conversation.  A reply may be expected.
        /// </summary>
        Normal
    }

    /// <summary>
    /// Describes the &lt;message&gt; element
    /// </summary>
    public class MessageElement
    {
        #region Static methods
        /// <summary>
        /// Converts a <see cref="MessageElementType"/> enumeration to a valid &lt;type&gt; attribute value
        /// </summary>
        /// <param name="elementType">The <see cref="MessageElementType"/> setting to convert</param>
        /// <returns>A valid string representation of the setting</returns>
        private static string MessageTypeToString(MessageElementType elementType)
        {
            switch (elementType)
            {
                case MessageElementType.Chat:
                    return "chat";
                case MessageElementType.Error:
                    return "error";
                case MessageElementType.GroupChat:
                    return "groupchat";
                case MessageElementType.Headline:
                    return "headline";
                case MessageElementType.Normal:
                    return "normal";
            }
            return "";
        }

        /// <summary>
        /// Converts the value of the "type" attribute of a &lt;message&gt; element to a <see cref="MessageElementType"/> enumeration
        /// </summary>
        /// <param name="type">The string value of the "type" attribute</param>
        /// <returns>A corresponding <see cref="MessageElementType"/> member</returns>
        private static MessageElementType StringToMessageType(string type)
        {
            switch (type)
            {
                case "chat":
                    return MessageElementType.Chat;
                case "error":
                    return MessageElementType.Error;
                case "groupchat":
                    return MessageElementType.GroupChat;
                case "headline":
                    return MessageElementType.Headline;
                case "normal":
                    return MessageElementType.Normal;
                default:
                    return MessageElementType.None;
            }
        }
        #endregion

        /// <summary>
        /// Creates a new &lt;message&gt; element with a plaintext body
        /// </summary>
        /// <param name="from">The <see cref="Jid"/> of the client</param>
        /// <param name="to">The <see cref="Jid"/> of the destination of the message</param>
        /// <param name="message">The plaintext message to send</param>
        /// <returns>A new &lt;message&gt; element</returns>
        public static Element Create(Jid from, Jid to, string message)
        {
            Dictionary<string, string> attributes = new Dictionary<string, string>();
            attributes["type"] = MessageTypeToString(MessageElementType.Chat);
            attributes["from"] = from.ToString();
            attributes["to"] = to.ToString(false);
            Element parent = Element.CreateElement("message", attributes);
            parent.Children.Add(Element.CreateElement("body", new Dictionary<string,string>(), message));
            return parent;
        }

        private readonly MessageElementType type = MessageElementType.None;
        private readonly Jid from;
        private readonly Jid to;
        private readonly string id;
        private readonly ReadOnlyCollection<Element> children;

        /// <summary>
        /// Initializes an MessageElement from a received <see cref="Element"/>
        /// </summary>
        /// <param name="element">The received <see cref="Element"/> containing the &lt;message&gt; stanza</param>
        public MessageElement(Element element)
        {
            string attrib = "";
            if (element.Attributes.TryGetValue("type", out attrib))
            {
                this.type = StringToMessageType(attrib);
            }
            if (element.Attributes.TryGetValue("from", out attrib))
            {
                this.from = new Jid(attrib);
            }
            if (element.Attributes.TryGetValue("to", out attrib))
            {
                this.to = new Jid(attrib);
            }
            id = element.Attributes["id"];
            this.children = new ReadOnlyCollection<Element>(element.Children);
        }

        /// <summary>
        /// Gets the value of the "type" attribute as a <see cref="MessageElementType"/>
        /// </summary>
        public MessageElementType Type
        {
            get { return type; }
        }

        /// <summary>
        /// Gets the <see cref="Jid"/> of the resource that sent the message
        /// </summary>
        public Jid From
        {
            get { return from; }
        }

        /// <summary>
        /// Gets the <see cref="Jid"/> of the destination of the message
        /// </summary>
        public Jid To
        {
            get { return to; }
        }

        /// <summary>
        /// Gets the ID of the message element
        /// </summary>
        public string ID
        {
            get { return id; }
        }

        /// <summary>
        /// Gets a collection of the message's child elements
        /// </summary>
        public ReadOnlyCollection<Element> Children
        {
            get { return children; }
        }
    }
}
